#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "Common.h"
#include "Window.h"
#include "Internals.h"
#include "CtrlClass.h"

#include "Assert.h"

#undef _TRACE_MSG
//#define _TRACE_MSG

static WNDPROC GetWndProc (HWND hWnd);

/* get main window pointer of a window, including desktop window */
static PMAINWIN getMainWindowPtr (HWND hWnd)
{
    PMAINWIN pWin;

    MG_CHECK_RET (MG_IS_WINDOW(hWnd), NULL);
    pWin = MG_GET_WINDOW_PTR (hWnd);

    return pWin->pMainWin;
}

/****************************** Message Queue Management ************************/
BOOL InitMsgQueue (PMSGQUEUE pMsgQueue, int iBufferLen)
{
    memset (pMsgQueue, 0, sizeof(MSGQUEUE));

    pMsgQueue->dwState = QS_EMPTY;

    if (iBufferLen <= 0)
    {
        iBufferLen = DEF_MSGQUEUE_LEN;
    }

    pMsgQueue->msg = malloc (sizeof (MSG) * iBufferLen);

    if (!pMsgQueue->msg)
    {
        return FALSE;
    }

    pMsgQueue->len = iBufferLen;

    return TRUE;
}

void DestroyMsgQueue (PMSGQUEUE pMsgQueue)
{
    if (pMsgQueue->msg)
        free (pMsgQueue->msg);
    pMsgQueue->msg = NULL;
}

/*
 * hWnd may belong to a different thread,
 * so this function should be thread-safe
 */
PMSGQUEUE GetMsgQueue (HWND hWnd)
{
    PMAINWIN pWin;

    pWin = getMainWindowPtr(hWnd);

    if (pWin)
        return pWin->pMessages;
    return NULL;
}

/* post a message to a message queue */
BOOL QueueMessage (PMSGQUEUE msg_que, PMSG msg)
{
    if ((msg_que->writepos + 1) % msg_que->len == msg_que->readpos)
    {
        return FALSE;
    }

    /* Write the data and advance write pointer */
    msg_que->msg [msg_que->writepos] = *msg;

    msg_que->writepos++;
    if (msg_que->writepos >= msg_que->len)
    {
        msg_que->writepos = 0;
    }

    return TRUE;
}

BOOL PeekMessage (PMSG pMsg, HWND hWnd, UINT uRemoveMsg)
{
    PMSGQUEUE pMsgQueue;
    
    if (!(pMsgQueue = GetMsgQueue(hWnd)))
    {
        return ERR_INV_HWND;
    }
    
    memset (pMsg, 0, sizeof(MSG));

    if (pMsgQueue->readpos != pMsgQueue->writepos)
    {
        *pMsg = pMsgQueue->msg[pMsgQueue->readpos];

        if (uRemoveMsg == PM_REMOVE)
        {
            pMsgQueue->readpos++;
            if (pMsgQueue->readpos >= pMsgQueue->len)
                pMsgQueue->readpos = 0;
        }

        return TRUE;
    }

    return FALSE;
}

BOOL GUIAPI GetMessage (PMSG pMsg, HWND hWnd)
{
    return PeekMessage (pMsg, hWnd, PM_REMOVE);
}

BOOL GUIAPI TranslateMessage (PMSG pMsg)
{
    if(pMsg->hwnd != HWND_DESKTOP)
    {
        if(pMsg->message == MSG_KEYDOWN &&(
                    (pMsg->wParam >= SCANCODE_1 && pMsg->wParam <= SCANCODE_0 ) ||
                    (pMsg->wParam >= SCANCODE_Q && pMsg->wParam <= SCANCODE_P ) ||
                    (pMsg->wParam >= SCANCODE_A && pMsg->wParam <= SCANCODE_L ) ||
                    (pMsg->wParam >= SCANCODE_Z && pMsg->wParam <= SCANCODE_M )))
        {
            PostMessage (pMsg->hwnd, MSG_CHAR, pMsg->wParam, pMsg->lParam);
        }
    }
}

int GUIAPI DispatchMessage (PMSG pMsg)
{
    WNDPROC WndProc;
    int iRet;

#ifdef _TRACE_MSG
    Uart0_Printf("Message, %s: hWnd: %x, wp:%x, lp : %lx\r\n",
                 Message2Str(pMsg->message),
                 pMsg->hwnd,
                 pMsg->wParam,
                 pMsg->lParam);
#endif

    if (pMsg->hwnd == HWND_INVALID) {
        return -1;
    }

    if (pMsg->hwnd == 0)
    {
        return -1;
    }

    if (!(WndProc = GetWndProc (pMsg->hwnd)))
        return -1;
        
#ifdef _TRACE_MSG
	Uart0_Printf("Message, %s done, hwnd  %x\r\n",
        Message2Str (pMsg->message), pMsg->hwnd);
#endif

    iRet = (*WndProc)(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);

#ifdef _TRACE_MSG
    Uart0_Printf("Message, %s done, return value: %x\r\n",
        Message2Str (pMsg->message), iRet);
#endif

    return iRet;
}

int GUIAPI ThrowAwayMessages (HWND hWnd)
{
    PMSG        pMsg;
    PMSGQUEUE   pMsgQueue = NULL;
    int         nCountN = 0;
    int         nCountS = 0;
    int         nCountP = 0;
    int         readpos;
    int         slot;

    if (hWnd && hWnd != HWND_INVALID) {
        PMAINWIN pWin = (PMAINWIN)hWnd;
        if (pWin->DataType == TYPE_HWND || pWin->DataType == TYPE_WINTODEL) {
            pMsgQueue = pWin->pMainWin->pMessages;
        }
    }

    if (pMsgQueue == NULL)
        return ERR_INV_HWND;

//    LOCK_MSGQ (pMsgQueue);


    readpos = pMsgQueue->readpos;
    while (readpos != pMsgQueue->writepos) {
        pMsg = pMsgQueue->msg + readpos;

        if (pMsg->hwnd == hWnd
                || GetMainWindowPtrOfControl (pMsg->hwnd) == (PMAINWIN)hWnd) {
            pMsg->hwnd = HWND_INVALID;
            nCountP ++;
        }
        
        readpos++;
        if (readpos >= pMsgQueue->len) 
            readpos = 0;
    }

#ifdef _TRACE_MSG
    Uart0_Printf ("ThrowAwayMessages: %d post messages thrown\r\n", nCountP);
#endif

//    UNLOCK_MSGQ (pMsgQueue);

    return nCountN + nCountS + nCountP;
}


const char *msgstr[] =
{
    "MSG_CREATE",
    "MSG_TIMER",
    "MSG_CHAR",
    "MSG_KEYUP",
    "MSG_KEYLONGPRESS",
    "MSG_KEYDOWN",
    "MSG_CHANGESIZE",
    "MSG_PAINT",
    "MSG_GETTEXTLENGTH",
    "MSG_GETTEXT",
    "MSG_SETTEXT",
    "MSG_DESTROY",
    "MSG_GETCTRLCLASSINFO",
    "MSG_NEWCTRLINSTANCE",
    "MSG_REMOVECTRLINSTANCE",
    "MSG_GETACTIVEMAIN",
    "MSG_ADDNEWMAINWIN",
    "MSG_REMOVEMAINWIN",   
    "MSG_USER",
};

const char* GUIAPI Message2Str (int message)
{
    if(message > MSG_USER)
    {
        return "MSG_USER";
    }
    else
    {
        return msgstr[message];
    }

    return "MSG_NULL";
}

/******************************************************************************/
static WNDPROC GetWndProc (HWND hWnd)
{
    return ((PMAINWIN)hWnd)->MainWindowProc;
}

int GUIAPI SendMessage (HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
    WNDPROC WndProc;

    //LG_ASSERT(0);
    MG_CHECK_RET (MG_IS_WINDOW(hWnd), -1);
    //LG_ASSERT(0);

    if ( !(WndProc = GetWndProc(hWnd)) )
        return ERR_INV_HWND;

    //LG_ASSERT(0);

    return (*WndProc)(hWnd, iMsg, wParam, lParam);

}

int GUIAPI PostMessage (HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
    PMSGQUEUE pMsgQueue;
    MSG msg;

    if (!(pMsgQueue = GetMsgQueue(hWnd)))
    {
        return ERR_INV_HWND;
    }

    msg.hwnd = hWnd;
    msg.message = iMsg;
    msg.wParam = wParam;
    msg.lParam = lParam;

    if (!QueueMessage(pMsgQueue, &msg))
        return ERR_QUEUE_FULL;

    return ERR_OK;
}
